﻿using System;
using System.Linq;
using System.IO;
using System.Collections.Generic;
using ShrinkWrap.Minifier;
using ShrinkWrap.Exceptions;

namespace ShrinkWrap.HttpHandler
{
    public class Aggregator
    {
        protected Func<String, String> mapPath { get; private set; }
        IMinifier minifier;

        /// <summary>
        /// A cap on the number of files to be aggregated.
        /// </summary>
        int maxConsituents;

        private CompositeUri compositeUri;
        public CompositeUri CompositeUri
        {
            get
            {
                return compositeUri;
            }

            set
            {
                if (value.ConstituentUris.Count() > maxConsituents)
                {
                    throw new ShrinkWrapException("Composite URI has more than the maximum number of constituents", null, compositeUri);
                }

                if (value.HasDifferingFileExtensions)
                {
                    throw new ShrinkWrapException("Files in composite URI must all have the same extension.", null, compositeUri);
                }

                compositeUri = value;
            }
        }

        public Aggregator(IMinifier minifier, CompositeUri composite, Func<String, String> mapPath, int maxConsituents)
        {
            this.mapPath = mapPath;
            this.minifier = minifier;
            this.maxConsituents = maxConsituents;

            // Note that the property is used when setting so that exceptions are thrown for illegal Uris
            this.CompositeUri = composite;
        }

        /// <summary>
        /// Gets an aggregated, minified representation of the URI.
        /// </summary>
        public virtual String GetAggregate()
        {
            String result = "";

            // Read, minify and aggregate each file from the CompositeUri
            try
            {
                Func<String, String> read = filename => File.ReadAllText(mapPath(filename));
                var rawData = compositeUri.ConstituentUris.Select(uri => read(uri.LocalPath));
                var minifieds = rawData.Select(raw => minifier.Minify(raw));
                result = aggregate(minifieds);
            }
            catch (FileNotFoundException ex)
            {
                // One of the constituent files not found
                throw new ShrinkWrapException("Constituent of '" + compositeUri.ToString() + "' not found.", ex, CompositeUri);
            }

            return result;
        }

        /// <summary>
        /// Collapses individual minifieds into a single aggregate.
        /// </summary>
        String aggregate(IEnumerable<String> minifieds)
        {
            return minifieds.Aggregate((agg, next) => agg + "\n" + next);
        }
    }
}
